"""凭证合并功能"""

from datetime import datetime, timedelta, date

from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_compare
from odoo.addons import decimal_precision as dp


class CombinedAccountMove(models.Model):
    _name = 'combined.account.move'
    # _inherit = ['account.move']
    _description = "合并后的凭证"

    #######拷贝自account.move
    name = fields.Char(string='Number', required=True, copy=False, default='/')
    ref = fields.Char(string='Reference', copy=False)
    date = fields.Date(required=True, index=True, default=fields.Date.context_today)
    journal_id = fields.Many2one('account.journal', string='Journal', required=True)
    currency_id = fields.Many2one('res.currency', string="Currency")

    partner_id = fields.Many2one('res.partner', string="Partner", readonly=True)
    amount = fields.Monetary()
    narration = fields.Text(string='Internal Note')
    company_id = fields.Many2one('res.company', related='journal_id.company_id', string='Company', store=True,
                                 readonly=True)
    matched_percentage = fields.Float('Percentage Matched', digits=0,
                                      readonly=True, help="Technical field used in cash basis method")
    # Dummy Account field to search on account.move by account_id
    dummy_account_id = fields.Many2one('account.account', related='line_ids.account_id', string='Account', store=False,
                                       readonly=True)
    tax_cash_basis_rec_id = fields.Many2one(
        'account.partial.reconcile',
        string='Tax Cash Basis Entry of',
        help="Technical field used to keep track of the tax cash basis reconciliation. "
             "This is needed when cancelling the source: it will post the inverse journal entry to cancel that part too.")
    auto_reverse = fields.Boolean(string='Reverse Automatically', default=False,
                                  help='If this checkbox is ticked, this entry will be automatically reversed at the reversal date you defined.')
    reverse_date = fields.Date(string='Reversal Date', help='Date of the reverse accounting entry.')
    reverse_entry_id = fields.Many2one('account.move', String="Reverse entry", store=True, readonly=True, copy=False)
    tax_type_domain = fields.Char(store=False,
                                  help='Technical field used to have a dynamic taxes domain on the form view.')
    ### 拷贝结束

    line_ids = fields.One2many('combined.account.move.line', 'move_id', string="凭证明细")
    state = fields.Selection([('draft', '草稿'), ('confirm', '确认'), ('cancel', '取消')],
                             string="状态", readonly=True)

    # @api.model
    # def create(self, val):
    #     val['name'] = 'xxx'
    #     return super().create(val)

    def get_print_data(self):
        return """<div>
            <h1>打印测试文档</h1> 
            <div>
                <table>
                    <thead>
                        <tr class="o_row"><th class="col-3">列1</th>
                        <th class="col-3">列2</th>
                        <th class="col-3">列3</th></tr>
                    </thead>
                    <tbody>
                        <tr><td>值1</td>
                        <td>值2</td>
                        <td>值3</td></tr>
                    </tbody>
                </table>
            </div>
        </div>"""

    @api.multi
    def confirm(self):
        draft_moves = self.filtered(lambda x: x.state == 'draft')
        draft_moves.write({"state": 'confirm'})
        for move in draft_moves:
            move.name = self.env['ir.sequence'].next_by_code('combined.account.move')

    @api.multi
    def cancel(self):
        draft_moves = self.filtered(lambda x: x.state == 'draft')
        draft_moves.write({"state": 'cancel'})

    @api.multi
    def unlink(self):
        confirmed_moves = self.filtered(lambda x: x.state == 'confirm')
        if confirmed_moves:
            raise UserError(f'已经确认的凭证，不能删除：{confirmed_moves.mapped("display_name")}')
        return super().unlink()

    @api.multi
    def view_origin_account_move(self):
        """查看被合并的凭证"""
        self.ensure_one()
        origin_move_ids = self.line_ids.mapped('origin_move_line_id.move_id')
        action = self.env.ref('account.action_move_journal_line').read()[0]
        action['domain'] = [('id', 'in', origin_move_ids.ids)]
        action['context'] = {}
        return action

    @api.multi
    def view_origin_account_move_line(self):
        self.ensure_one()
        origin_move_line_ids = self.line_ids.mapped('origin_move_line_id')
        action = self.env.ref('account.action_account_moves_all_a').read()[0]
        action['domain'] = [('id', 'in', origin_move_line_ids.ids)]
        # action['context'] = {}
        return action


class CombinedAccountMoveLine(models.Model):
    _name = 'combined.account.move.line'
    # _inherit = ['account.move.line']
    _description = "合并后的凭证明细"

    # 拷贝自account.move.line
    name = fields.Char(string="Label")
    quantity = fields.Float(digits=dp.get_precision('Product Unit of Measure'),
                            help="The optional quantity expressed by this line, eg: number of product sold. The quantity is not a legal requirement but is very useful for some reports.")
    product_uom_id = fields.Many2one('uom.uom', string='Unit of Measure')
    product_id = fields.Many2one('product.product', string='Product')
    debit = fields.Monetary(default=0.0, currency_field='company_currency_id')
    credit = fields.Monetary(default=0.0, currency_field='company_currency_id')
    balance = fields.Monetary(currency_field='company_currency_id',
                              help="Technical field holding the debit - credit in order to open meaningful graph views from reports")
    debit_cash_basis = fields.Monetary(currency_field='company_currency_id',)
    credit_cash_basis = fields.Monetary(currency_field='company_currency_id')
    balance_cash_basis = fields.Monetary(currency_field='company_currency_id',
                                         help="Technical field holding the debit_cash_basis - credit_cash_basis in order to open meaningful graph views from reports")
    amount_currency = fields.Monetary(default=0.0,
                                      help="The amount expressed in an optional other currency if it is a multi-currency entry.")
    company_currency_id = fields.Many2one('res.currency', related='company_id.currency_id', string="Company Currency",
                                          readonly=True,
                                          help='Utility field to express amount currency', store=True)
    currency_id = fields.Many2one('res.currency', string='Currency',
                                  help="The optional other currency if it is a multi-currency entry.")
    amount_residual = fields.Monetary(string='Residual Amount',  currency_field='company_currency_id',
                                      help="The residual amount on a journal item expressed in the company currency.")
    amount_residual_currency = fields.Monetary(string='Residual Amount in Currency',
                                               help="The residual amount on a journal item expressed in its currency (possibly not the company currency).")
    tax_base_amount = fields.Monetary(string="Base Amount", currency_field='company_currency_id')
    account_id = fields.Many2one('account.account', string='Account', required=True, index=True,
                                 ondelete='restrict', domain=[('deprecated', '=', False)],
                                 default=lambda self: self._context.get('account_id', False))
    narration = fields.Text(related='move_id.narration', string='Narration', readonly=False)
    ref = fields.Char(related='move_id.ref', string='Reference', store=True, copy=False, index=True, readonly=False)
    payment_id = fields.Many2one('account.payment', string="Originator Payment", help="Payment that created this entry",
                                 copy=False)
    statement_line_id = fields.Many2one('account.bank.statement.line', index=True,
                                        string='Bank statement line reconciled with this entry', copy=False,
                                        readonly=True)
    statement_id = fields.Many2one('account.bank.statement', related='statement_line_id.statement_id',
                                   string='Statement', store=True,
                                   help="The bank statement used for bank reconciliation", index=True, copy=False)
    reconciled = fields.Boolean()
    full_reconcile_id = fields.Many2one('account.full.reconcile', string="Matching Number", copy=False)
    matched_debit_ids = fields.One2many('account.partial.reconcile', 'credit_move_id', String='Matched Debits',
                                        help='Debit journal items that are matched with this journal item.')
    matched_credit_ids = fields.One2many('account.partial.reconcile', 'debit_move_id', String='Matched Credits',
                                         help='Credit journal items that are matched with this journal item.')
    journal_id = fields.Many2one('account.journal', related='move_id.journal_id', string='Journal', readonly=False,
                                 index=True, store=True, copy=False)  # related is required
    blocked = fields.Boolean(string='No Follow-up', default=False,
                             help="You can check this box to mark this journal item as a litigation with the associated partner")
    date_maturity = fields.Date(string='Due date', index=True, required=True, copy=False,
                                help="This field is used for payable and receivable journal entries. You can put the limit date for the payment of this line.")
    date = fields.Date(related='move_id.date', string='Date', index=True, store=True, copy=False,
                       readonly=False)  # related is required
    analytic_line_ids = fields.One2many('account.analytic.line', 'move_id', string='Analytic lines',
                                        oldname="analytic_lines")
    tax_ids = fields.Many2many('account.tax', string='Taxes Applied',
                               domain=['|', ('active', '=', False), ('active', '=', True)],
                               help="Taxes that apply on the base amount")
    tax_line_id = fields.Many2one('account.tax', string='Originator tax', ondelete='restrict',
                                  help="Indicates that this journal item is a tax line")
    analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', index=True)
    analytic_tag_ids = fields.Many2many('account.analytic.tag', string='Analytic Tags')
    company_id = fields.Many2one('res.company', related='account_id.company_id', string='Company', store=True,
                                 readonly=True)
    counterpart = fields.Char("Counterpart",
                              help="Compute the counter part accounts of this journal item for this journal entry. This can be needed in reports.")

    # TODO: put the invoice link and partner_id on the account_move
    invoice_id = fields.Many2one('account.invoice', oldname="invoice")
    partner_id = fields.Many2one('res.partner', string='Partner', ondelete='restrict')
    user_type_id = fields.Many2one('account.account.type', related='account_id.user_type_id', index=True, store=True,
                                   oldname="user_type", readonly=True)
    tax_exigible = fields.Boolean(string='Appears in VAT report', default=True,
                                  help="Technical field used to mark a tax line as exigible in the vat report or not (only exigible journal items are displayed). By default all new journal items are directly exigible, but with the feature cash_basis on taxes, some will become exigible only when the payment is recorded.")
    parent_state = fields.Char( help="State of the parent account.move")

    recompute_tax_line = fields.Boolean(store=False,
                                        help="Technical field used to know if the tax_ids field has been modified in the UI.")
    tax_line_grouping_key = fields.Char(store=False, string='Old Taxes',
                                        help="Technical field used to store the old values of fields used to compute tax lines (in account.move form view) between the moment the user changed it and the moment the ORM reflects that change in its one2many")

    # 拷贝结束

    move_id = fields.Many2one('combined.account.move', string="合并的凭证", ondelete="cascade",
                              index=True, required=True, auto_join=True)
    origin_move_line_id = fields.Many2one('account.move.line', string="原凭证明细")
    # state = fields.Selection(related=move_id.state, store=True)

    @api.model
    def combine_account_move_line(self, account_move_lines):
        """合并凭证明细"""
        if not account_move_lines:
            raise UserError('未选择要合并的凭证，或者所选凭证已经合并')
        self.check_account_move_line(account_move_lines)
        ref = ''
        for move in account_move_lines.mapped('move_id'):
            ref += ((move.ref or '') + ',')
        default = {
            'line_ids': [],
            'date': fields.Date.today(),
            'ref': ref
        }
        data = account_move_lines[0].move_id.copy_data(default=default)[0]
        line_datas = data['line_ids']
        for line in account_move_lines:
            default = {'move_id': False, 'state': False, 'date_maturity': line.date_maturity}
            line_data = line.copy_data(default=default)[0]
            line_data['origin_move_line_id'] = line.id
            line_datas.append((0, 0, line_data))

        combined_move_id = self.env['combined.account.move'].create(data)
        # account_move_lines.write({"is_combine": True})
        return combined_move_id

    @staticmethod
    def check_account_move_line(account_move_lines):
        """检查待合并的凭证明细"""
        # 必须属于同一客户
        # 必须属于类型
        # 必须属于同一月份
        # 必须未合并过
        # 必须借贷平衡
        sample_line = account_move_lines[0]
        partner_id = sample_line.partner_id
        journal_id = sample_line.journal_id
        start = sample_line.date.replace(day=1)
        end = date(year=sample_line.date.year, month=sample_line.date.month+1, day=1) - timedelta(days=1)
        credit = 0
        debit = 0

        err_msg = '所选凭证的”{field_name}“不一致：{sample_value}， {test_value}！'
        for line in account_move_lines:
            if line.partner_id != partner_id:
                raise UserError(err_msg.format(field_name="合作伙伴",
                                               sample_value=partner_id.name,
                                               test_value=line.partner_id.name))
            elif line.journal_id != journal_id:
                raise UserError(err_msg.format(field_name="日记账",
                                               sample_value=journal_id.name,
                                               test_value=line.journal_id.name))
            elif not (start < line.date < end):
                raise UserError(err_msg.format(field_name="日期",
                                               sample_value=f"{start.year}年{start.month}月",
                                               test_value=f"{end.year}年{end.month}月"))
            elif line.is_combined:
                raise UserError(err_msg.format(field_name="部分已经合并，不能重复合并",
                                               sample_value=f"{line.display_name}",
                                               test_value=""))
            credit += line.credit
            debit += line.debit

        if float_compare(credit, debit, precision_digits=2) != 0:
            raise UserError(f'所选凭证的总借款金额不一致：借方{debit} 贷方{credit}')




